/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.flexengine.server;

import com.flexengine.logger.Logger;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

/**
 * @author Luann R. Athayde
 * @version 1.0
 * @since 1.0
 */
public abstract class FlexEngineSession implements Runnable {
    
    protected Thread           sessionThread;
    
    protected long             sessionID;
    protected Socket           sessionSocket;
    protected FlexEngineStream stream;
    protected boolean          running;
    
    public FlexEngineSession() {
        this(null);
    }
    public FlexEngineSession(Socket sessionSocket) {
        this.sessionID = 0;
        this.running = false;
        try {
            this.sessionSocket = sessionSocket;
            this.startStream();
        } catch(Exception e) {
            Logger.logError(getClass().getSimpleName()+" - startStream(): "+e);
        }
    }
    
    private void startStream() {
        try {
            Logger.logMessage(getClass().getSimpleName()+" - startStream(): Creating stream...");
            Logger.logMessage(getClass().getSimpleName()+" - startStream(): in closed  = "+sessionSocket.isInputShutdown());
            Logger.logMessage(getClass().getSimpleName()+" - startStream(): out closed = "+sessionSocket.isOutputShutdown());
            ObjectOutputStream out = new ObjectOutputStream(sessionSocket.getOutputStream());
            ObjectInputStream  in  = new ObjectInputStream(sessionSocket.getInputStream());
            this.stream = new FlexEngineStream(in,out);
            Logger.logMessage(getClass().getSimpleName()+" - startStream(): stream = "+stream);
        } catch (IOException e) {
            this.stream = null;
            this.sessionSocket = null;
        }
    }
    
    public boolean connect(String host, int port) {
        try {
            Logger.logMessage(getClass().getSimpleName()+" - connect("+host+","+port+"): Connecting...");
            sessionSocket = new Socket(host, port);
            Logger.logMessage(getClass().getSimpleName()+" - connect(): sock  = "+sessionSocket);
            Logger.logMessage(getClass().getSimpleName()+" - connect(): isCon = "+sessionSocket.isConnected());
            this.startStream();
            // -- Verifing the version...
            FlexMessage msg = new FlexMessage(FlexEngineServer.SERVER_VERSION, "version");
            Logger.logMessage(getClass().getSimpleName()+" - connect(): Sending version check...");
            if( getStream().write(msg) ) {
                msg = getStream().read();
                if( msg.getAction()==FlexEngineServer.SERVER_VERSION && msg.getMessage().equals("VERSION_OK") ) {
                    // --
                    Logger.logMessage(getClass().getSimpleName()+" - connect(): VERSION CHECKED!");
                    return true;
                } else if( msg.getMessage().equals("WRONG_VERSION") ) {
                    // --
                    Logger.logWarning(getClass().getSimpleName()+" - connect(): Wrong version!");
                } else if( msg.getMessage().equals("NOT_HAVE_LISTENER") ) {
                    Logger.logWarning(getClass().getSimpleName()+" - connect(): The server does'nt have a listener!");
                } else if( msg.getMessage().equals("MAX_USERS_REACHED") ) {
                    Logger.logWarning(getClass().getSimpleName()+" - connect(): "
                            + "The max connections has been reached! "
                            + "Total connections: "+msg.getAction());
                }
            } else {
                Logger.logWarning(getClass().getSimpleName()+" - connect(): Wasn't possible to check the version...");
            }
            disconnect();
        } catch(IOException e) {
            Logger.logError(getClass().getSimpleName()+" - connect(): "+e);
        }
        return false;
    }
    public boolean connect(String host) {
        return connect(host, FlexEngineServer.DEFAULT_PORT);
    }
    public boolean disconnect() {
        try {
            running = false;
            Logger.logMessage(getClass().getSimpleName()+" - disconnect(): Sending disconenct information...");
            FlexMessage msg = new FlexMessage(-1, "disconnect");
            if( stream.write(msg) ) {
                Logger.logMessage(getClass().getSimpleName()+" - disconnect(): Information sended...");
            }
            stream.close();
            stream = null;
            sessionSocket.close();
            boolean ret = sessionSocket.isClosed();
            sessionSocket = null;
            Logger.logMessage(getClass().getSimpleName()+" - disconnect(): Session["+getSessionID()+"] disconnected!");
            return ret;
        } catch(IOException e) {
            Logger.logError(getClass().getSimpleName()+" - disconnect(): "+e);
        }
        return false;
    }
    
    public boolean isConnected() {
        try {
            return sessionSocket.isConnected();
        } catch(Exception e) {
            Logger.logError(getClass().getSimpleName()+" - isConnect(): "+e);
        }
        return false;
    }
    public boolean isInputShutdown() {
        try {
            return sessionSocket.isInputShutdown();
        } catch(Exception e) {
            Logger.logError(getClass().getSimpleName()+" - isConnect(): "+e);
        }
        return false;
    }
    public boolean isOutputShutdown() {
        try {
            return sessionSocket.isOutputShutdown();
        } catch(Exception e) {
            Logger.logError(getClass().getSimpleName()+" - isConnect(): "+e);
        }
        return false;
    }
    
    public long getSessionID() {
        return sessionID;
    }

    public void setSessionID(long sessionID) {
        this.sessionID = sessionID;
    }

    public Socket getSessionSocket() {
        return sessionSocket;
    }

    public void setSessionSocket(Socket sessionSocket) {
        this.sessionSocket = sessionSocket;
    }

    public FlexEngineStream getStream() {
        return stream;
    }

    public void setStream(FlexEngineStream stream) {
        this.stream = stream;
    }
    
    public void start() {
        if( !running && isConnected() ) {
            if( sessionThread!=null ) {
                sessionThread.interrupt();
            }
            running = true;
            sessionThread = null;
            sessionThread = new Thread(this,this.getClass().getName());
            sessionThread.start();
        }
    }
    
    public void stop() {
        if( sessionThread!=null ) {
            sessionThread.interrupt();
        }
        sessionThread = null;
    }
    
    @Override
    public void run() {
        try {
            Logger.logMessage("FlexEngineSession - run(): Initializing the received messages loop...");
            while( running ) {
                FlexMessage msg = getStream().read();
                if( msg.getAction()==-1 && msg.getMessage().equals("disconnect")  ) {
                    disconnect();
                } else {
                    messageReceived(msg);
                }
            }
            Logger.logMessage("FlexEngineSession - run(): Fineshed!");
        } catch(Exception e) {
            disconnect();
        }
        running = false;
    }
    
    public abstract void messageReceived(FlexMessage msg);

    public boolean isRunning() {
        return running;
    }

    @Override
    public String toString() {
        return this.getClass().getSimpleName()+"{" + "sessionID=" + sessionID + ", sessionSocket=" + sessionSocket + ", stream=" + stream + '}';
    }
    
}
